﻿// <copyright file="Compression.cs" company="Sinclair Community College">
// Copyright 2010 Sinclair Community College
// </copyright>
// <originalAuthor>Patrick S. Seymour (patrick.seymour@gmail.com)</originalAuthor>

namespace SinclairCC.AppV
{
    using System;

    /// <summary>
    /// This class defines some common functions related to the decompression of data.
    /// </summary>
    internal static class Compression
    {
        /// <summary>
        /// Decompresses the contents of the specified BinaryReader, which has been compressed
        /// using the given compression method.
        /// </summary>
        /// <param name="rd">
        /// The BinaryReader whose contents are to be decompressed.
        /// </param>
        /// <param name="compressedLength">
        /// The number of bytes to read from the BinaryReader and then decompress.
        /// </param>
        /// <param name="compressionType">
        /// The type of compression used on the original BinaryReader object.
        /// </param>
        /// <returns>
        /// Returns a BinaryReader object containing the decompressed contents of the BinaryReader
        /// that was passed to the function.
        /// </returns>
        public static System.IO.BinaryReader GetDecompressedReader(System.IO.BinaryReader rd, long compressedLength, CompressionMethod compressionType)
        {
            System.IO.MemoryStream compressedMemoryStream = GetBytesFromReader(rd, compressedLength);
            System.IO.Stream inflatorStream = null;

            switch (compressionType)
            {
                case CompressionMethod.bzip2:
                    inflatorStream = new ICSharpCode.SharpZipLib.BZip2.BZip2InputStream(compressedMemoryStream);
                    break;

                case CompressionMethod.ZLib:
                    inflatorStream = new ICSharpCode.SharpZipLib.Zip.Compression.Streams.InflaterInputStream(compressedMemoryStream);
                    break;
            }

            System.IO.MemoryStream decompressedMemoryStream = null;
            System.IO.BinaryReader reader = null;

            if (inflatorStream != null)
            {
                decompressedMemoryStream = ReadStream(inflatorStream);

                inflatorStream.Close();
                inflatorStream.Dispose();

                reader = new System.IO.BinaryReader(decompressedMemoryStream);
            }

            compressedMemoryStream.Close();
            compressedMemoryStream.Dispose();

            return reader;
        }

        /// <summary>
        /// Reads the specified number of bytes from a BinaryReader into a MemoryStream.
        /// </summary>
        /// <param name="reader">
        /// The BinaryReader object from which the bytes are to be read.
        /// </param>
        /// <param name="byteCount">
        /// The number of bytes to read from the BinaryReader.
        /// </param>
        /// <returns>
        /// Returns a MemoryStream object containing the bytes from the file. The position
        /// within the stream is set to the beginning of the stream.
        /// </returns>
        private static System.IO.MemoryStream GetBytesFromReader(System.IO.BinaryReader reader, long byteCount)
        {
            System.IO.MemoryStream returnStream = new System.IO.MemoryStream();

            while (byteCount > 0)
            {
                returnStream.WriteByte(reader.ReadByte());
                byteCount--;
            }

            // Seek to the beginning of the stream.
            returnStream.Seek(0, System.IO.SeekOrigin.Begin);

            return returnStream;
        }

        /// <summary>
        /// Reads the specified stream into a MemoryStream.
        /// </summary>
        /// <param name="inflatorStream">
        /// The stream to be read into the memory stream.
        /// </param>
        /// <returns>
        /// Returns a MemoryStream object containing the contents of the passed
        /// stream. The position within the stream is set to the beginning of the stream.
        /// </returns>
        private static System.IO.MemoryStream ReadStream(System.IO.Stream inflatorStream)
        {
            System.IO.MemoryStream memStream = new System.IO.MemoryStream();

            byte[] buffer = new byte[1024];
            int bytesRead;
            do
            {
                bytesRead = inflatorStream.Read(buffer, 0, buffer.Length);
                memStream.Write(buffer, 0, bytesRead);
            }
            while (bytesRead > 0);            

            // Seek to the beginning of the stream.
            memStream.Seek(0, System.IO.SeekOrigin.Begin);

            return memStream;
        }
    }
}
